/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Attribute location test
 *//*--------------------------------------------------------------------*/

#include "es3fAttribLocationTests.hpp"

#include "glsAttributeLocationTests.hpp"

#include "glw.h"

using namespace deqp::gls::AttributeLocationTestUtil;
using std::vector;

namespace deqp
{
namespace gles3
{
namespace Functional
{

TestCaseGroup* createAttributeLocationTests	(Context& context)
{
	const AttribType	types[] =
	{
		AttribType("float",		1,  GL_FLOAT),
		AttribType("vec2",		1,  GL_FLOAT_VEC2),
		AttribType("vec3",		1,  GL_FLOAT_VEC3),
		AttribType("vec4",		1,  GL_FLOAT_VEC4),

		AttribType("mat2",		2,  GL_FLOAT_MAT2),
		AttribType("mat3",		3,  GL_FLOAT_MAT3),
		AttribType("mat4",		4,  GL_FLOAT_MAT4),

		AttribType("int",		1,	GL_INT),
		AttribType("ivec2",		1,	GL_INT_VEC2),
		AttribType("ivec3",		1,	GL_INT_VEC3),
		AttribType("ivec4",		1,	GL_INT_VEC4),

		AttribType("uint",		1,	GL_UNSIGNED_INT),
		AttribType("uvec2",		1,	GL_UNSIGNED_INT_VEC2),
		AttribType("uvec3",		1,	GL_UNSIGNED_INT_VEC3),
		AttribType("uvec4",		1,	GL_UNSIGNED_INT_VEC4),

		AttribType("mat2x2",	2,	GL_FLOAT_MAT2),
		AttribType("mat2x3",	2,	GL_FLOAT_MAT2x3),
		AttribType("mat2x4",	2,	GL_FLOAT_MAT2x4),

		AttribType("mat3x2",	3,	GL_FLOAT_MAT3x2),
		AttribType("mat3x3",	3,	GL_FLOAT_MAT3),
		AttribType("mat3x4",	3,	GL_FLOAT_MAT3x4),

		AttribType("mat4x2",	4,	GL_FLOAT_MAT4x2),
		AttribType("mat4x3",	4,	GL_FLOAT_MAT4x3),
		AttribType("mat4x4",	4,	GL_FLOAT_MAT4)
	};

	const AttribType	es2Types[] =
	{
		AttribType("float",	1,  GL_FLOAT),
		AttribType("vec2",	1,  GL_FLOAT_VEC2),
		AttribType("vec3",	1,  GL_FLOAT_VEC3),
		AttribType("vec4",	1,  GL_FLOAT_VEC4),

		AttribType("mat2",	2,  GL_FLOAT_MAT2),
		AttribType("mat3",	3,  GL_FLOAT_MAT3),
		AttribType("mat4",	4,  GL_FLOAT_MAT4)
	};

	TestCaseGroup* const root = new TestCaseGroup (context, "attribute_location", "Attribute location tests");

	// Basic bind attribute tests
	{
		TestCaseGroup* const bindAttributeGroup = new TestCaseGroup(context, "bind", "Basic bind attribute location tests.");

		root->addChild(bindAttributeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];
			bindAttributeGroup->addChild(new gls::BindAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Bind max number of attributes
	{
		TestCaseGroup* const bindMaxAttributeGroup = new TestCaseGroup(context, "bind_max_attributes", "Use bind with maximum number of attributes.");

		root->addChild(bindMaxAttributeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];
			bindMaxAttributeGroup->addChild(new gls::BindMaxAttributesTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test aliasing
	{
		TestCaseGroup* const aliasingGroup = new TestCaseGroup(context, "bind_aliasing", "Test binding aliasing locations.");

		root->addChild(aliasingGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(es2Types); typeNdx++)
		{
			const AttribType& type = es2Types[typeNdx];

			// Simple aliasing cases
			aliasingGroup->addChild(new gls::BindAliasingAttributeTest(context.getTestContext(), context.getRenderContext(), type));

			// For types which occupy more than one location. Alias second location.
			if (type.getLocationSize() > 1)
				aliasingGroup->addChild(new gls::BindAliasingAttributeTest(context.getTestContext(), context.getRenderContext(), type, 1));

			// Use more than maximum attributes with aliasing
			aliasingGroup->addChild(new gls::BindMaxAliasingAttributeTest(context.getTestContext(), context.getRenderContext(), type));

			// Use more than maximum attributes but inactive
			aliasingGroup->addChild(new gls::BindInactiveAliasingAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test filling holes in attribute location
	{
		TestCaseGroup* const holeGroup = new TestCaseGroup(context, "bind_hole", "Bind all, but one attribute and leave hole in location space for it.");

		root->addChild(holeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];

			// Bind first location, leave hole size of type and fill rest of locations
			holeGroup->addChild(new gls::BindHoleAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test binding at different times
	{
		TestCaseGroup* const bindTimeGroup = new TestCaseGroup(context, "bind_time", "Bind time tests. Test binding at different stages.");

		root->addChild(bindTimeGroup);

		bindTimeGroup->addChild(new gls::PreAttachBindAttributeTest(context.getTestContext(), context.getRenderContext()));
		bindTimeGroup->addChild(new gls::PreLinkBindAttributeTest(context.getTestContext(), context.getRenderContext()));
		bindTimeGroup->addChild(new gls::PostLinkBindAttributeTest(context.getTestContext(), context.getRenderContext()));
		bindTimeGroup->addChild(new gls::BindRelinkAttributeTest(context.getTestContext(), context.getRenderContext()));
		bindTimeGroup->addChild(new gls::BindReattachAttributeTest(context.getTestContext(), context.getRenderContext()));
	}

	// Basic layout location attribute tests
	{
		TestCaseGroup* const layoutAttributeGroup = new TestCaseGroup(context, "layout", "Basic layout location tests.");

		root->addChild(layoutAttributeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];
			layoutAttributeGroup->addChild(new gls::LocationAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test max attributes with layout locations
	{
		TestCaseGroup* const layoutMaxAttributeGroup = new TestCaseGroup(context, "layout_max_attributes", "Maximum attributes used with layout location qualifiers.");

		root->addChild(layoutMaxAttributeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];
			layoutMaxAttributeGroup->addChild(new gls::LocationMaxAttributesTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test filling holes in attribute location
	{
		TestCaseGroup* const holeGroup = new TestCaseGroup(context, "layout_hole", "Define layout location for all, but one attribute consuming max attribute locations.");

		root->addChild(holeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];

			// Location first location, leave hole size of type and fill rest of locations
			holeGroup->addChild(new gls::LocationHoleAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Basic mixed mixed attribute tests
	{
		TestCaseGroup* const mixedAttributeGroup = new TestCaseGroup(context, "mixed", "Basic mixed location tests.");

		root->addChild(mixedAttributeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];
			mixedAttributeGroup->addChild(new gls::MixedAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	{
		TestCaseGroup* const mixedMaxAttributeGroup = new TestCaseGroup(context, "mixed_max_attributes", "Maximum attributes used with mixed binding and layout qualifiers.");

		root->addChild(mixedMaxAttributeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];
			mixedMaxAttributeGroup->addChild(new gls::MixedMaxAttributesTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test mixed binding at different times
	{
		TestCaseGroup* const mixedTimeGroup = new TestCaseGroup(context, "mixed_time", "Bind time tests. Test binding at different stages.");

		root->addChild(mixedTimeGroup);

		mixedTimeGroup->addChild(new gls::PreAttachMixedAttributeTest(context.getTestContext(), context.getRenderContext()));
		mixedTimeGroup->addChild(new gls::PreLinkMixedAttributeTest(context.getTestContext(), context.getRenderContext()));
		mixedTimeGroup->addChild(new gls::PostLinkMixedAttributeTest(context.getTestContext(), context.getRenderContext()));
		mixedTimeGroup->addChild(new gls::MixedRelinkAttributeTest(context.getTestContext(), context.getRenderContext()));
		mixedTimeGroup->addChild(new gls::MixedReattachAttributeTest(context.getTestContext(), context.getRenderContext()));
	}

	{
		TestCaseGroup* const holeGroup = new TestCaseGroup(context, "mixed_hole", "Use layout location qualifiers and binding. Leave hole in location space for only free attribute.");

		root->addChild(holeGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];

			holeGroup->addChild(new gls::MixedHoleAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test hole in location space that moves when relinking
	{
		TestCaseGroup* const relinkBindHoleGroup = new TestCaseGroup(context, "bind_relink_hole", "Test relinking with moving hole in attribute location space.");

		root->addChild(relinkBindHoleGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];

			relinkBindHoleGroup->addChild(new gls::BindRelinkHoleAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	// Test hole in location space that moves when relinking
	{
		TestCaseGroup* const relinkMixedHoleGroup = new TestCaseGroup(context, "mixed_relink_hole", "Test relinking with moving hole in attribute location space.");

		root->addChild(relinkMixedHoleGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			const AttribType& type = types[typeNdx];

			relinkMixedHoleGroup->addChild(new gls::MixedRelinkHoleAttributeTest(context.getTestContext(), context.getRenderContext(), type));
		}
	}

	return root;
}

} // Functional
} // gles3
} // deqp
